/*
 * Copyright 2007-2009 Thomas Krause (thomas@berlin-reloaded.de)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gidoo.cms.login;

import javax.jcr.LoginException;
import org.gidoo.cms.helper.Crypto;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.query.QueryManager;
import org.apache.wicket.Request;
import org.apache.wicket.authentication.AuthenticatedWebSession;
import org.apache.wicket.authorization.strategies.role.Roles;
import org.gidoo.cms.GidooCMS;

/**
 * 
 * @author <a href="mailto:thomas@berlin-reloaded.de">Thomas Krause</a>
 */
public class GidooCMSSession extends AuthenticatedWebSession
{

  public static final String ROLE_PUBLISHER = "PUBLISHER";
  public static final String ROLE_EDITOR = "EDITOR";
  public static final String ANONYMOUS = "anonymous";
  public static final SimpleCredentials ANONYMOUS_CREDENTIALS =
    new SimpleCredentials(ANONYMOUS, new char[0]);
  private String lastVisitedFolder;
  private String username;
  transient private Session jcrSession;

  public GidooCMSSession(Request req)
  {
    super(req);
  }

  public boolean authenticate(final String username, final String password)
  {
    boolean result = false;
    try
    {
      String usernamedFixed = username.replaceAll("/", "");
      GidooCMS app = (GidooCMS) getApplication();

      Session tmpSession = app.getRepository().login(GidooCMSSession.ANONYMOUS_CREDENTIALS);
      Node usersNode =
        tmpSession.getRootNode().getNode("users");

      if(usersNode.hasNode(usernamedFixed))
      {
        String md5 = Crypto.calculateSHAHash(password);

        Node tempUserNode = usersNode.getNode(usernamedFixed);

        result = md5.equals(tempUserNode.getProperty("passwordhash").getString());

        if(result)
        {
          if(jcrSession != null)
          {
            jcrSession.logout();
          }

          this.username = usernamedFixed;

          jcrSession = app.getRepository().login(
            new SimpleCredentials(usernamedFixed, password.toCharArray()));
        }
      }

      tmpSession.logout();
    }
    catch(Exception ex)
    {
      jcrSession = null;
      result = false;
      Logger.getLogger(GidooCMSSession.class.getName()).log(Level.SEVERE, null, ex);
    }
    return result;
  }

  @Override
  public void signOut()
  {
    super.signOut();

    jcrSession.logout();
  }

  @Override
  protected void finalize() throws Throwable
  {
    super.finalize();

    if(jcrSession != null)
    {
      jcrSession.logout();
    }

  }

  /** 
   * Return the JCR session associated with this session.
   * 
   * @return The session or null if not logged in;
   */
  public Session getJcrSession()
  {
    if(jcrSession == null || !jcrSession.isLive())
    {
      if(jcrSession != null)
      {
        jcrSession.logout();
      }

      try
      {
        GidooCMS app = (GidooCMS) getApplication();
        if(app != null && app.getRepository() != null)
        {
          jcrSession = app.getRepository().login(GidooCMSSession.ANONYMOUS_CREDENTIALS);

          if(isFirstRuntime())
          {
            jcrSession.logout();

            jcrSession = app.getRepository().login(
              new SimpleCredentials("firstUser", new char[0]));
          }
        }
      }
      catch(LoginException ex)
      {
        Logger.getLogger(GidooCMSSession.class.getName()).log(Level.SEVERE, null, ex);
      }
      catch(RepositoryException ex)
      {
        Logger.getLogger(GidooCMSSession.class.getName()).log(Level.SEVERE, null, ex);
      }
    }

    return jcrSession;
  }

  /** 
   * Return the root node of the JCR session.
   * 
   * @return The root node or null if not logged in.
   */
  public Node getJcrRootNode() throws RepositoryException
  {
    if(getJcrSession() != null)
    {
      return getJcrSession().getRootNode();
    }
    return null;
  }

  public Roles getRoles()
  {
    // fallback solution if no user is created yet
    if(isFirstRuntime())
    {
      Roles r = new Roles();
      r.add(Roles.ADMIN);
      return r;
    }

    if(isSignedIn())
    {
      Roles r = new Roles();

      try
      {
        Node userInfo = getJcrRootNode().getNode("users").getNode(getUsername());

        try
        {

          Node publishNode = userInfo.getNode("publish");

          if(publishNode != null && publishNode.getNodes().hasNext())
          {
            r.add(ROLE_PUBLISHER);
          }
        }
        catch(PathNotFoundException ex)
        {
        }

        try
        {
          Node editNode = userInfo.getNode("edit");

          if(editNode != null && editNode.getNodes().hasNext())
          {
            r.add(ROLE_EDITOR);
          }
        }
        catch(PathNotFoundException ex)
        {
        }
        if(userInfo.getProperty("isadmin").getBoolean())
        {
          r.add(Roles.ADMIN);
        }

        return r;
      }
      catch(RepositoryException ex)
      {
        Logger.getLogger(GidooCMSSession.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
    return null;
  }

  /** Checks wether this CMS is the first time in use (there are no users or pages) */
  public boolean isFirstRuntime()
  {
    try
    {
      Node rNode = getJcrRootNode();
      if(rNode != null)
      {
        Node users = rNode.getNode("users");
        NodeIterator itUsers = users.getNodes();

        Node sections = rNode.getNode("sections");
        NodeIterator itSections = sections.getNodes();
        if(!itUsers.hasNext() && !itSections.hasNext())
        {
          return true;
        }
      }
    }
    catch(RepositoryException ex)
    {
      Logger.getAnonymousLogger().log(Level.SEVERE, null, ex);
    }
    return false;
  }

  /** Returns the username of the current logged in user or null */
  public String getUsername()
  {
    return username;
  }

  /** Get the last visited folder of this session. */
  public Node getLastVisitedFolder()
  {
    Node result = null;
    try
    {
      result = getJcrRootNode().getNode(lastVisitedFolder.substring(1));
    }
    catch(Exception ex)
    {
      result = null;
    }

    return result;
  }

  /** Set the last visited folder of this session. */
  public void setLastVisitedFolder(Node lastVisitedFolder)
  {
    if(lastVisitedFolder == null)
    {
      this.lastVisitedFolder = null;
    }
    else
    {
      try
      {
        this.lastVisitedFolder = lastVisitedFolder.getPath();
      }
      catch(RepositoryException ex)
      {
        this.lastVisitedFolder = null;
      }
    }
  }

  /** Get the query manager or null if there was a problem contacting the JCR repository */
  public QueryManager getQueryManager() throws RepositoryException
  {
    Session s = getJcrSession();
    if(s != null && s.getWorkspace() != null)
    {
      return s.getWorkspace().getQueryManager();
    }
    else
    {
      return null;
    }
  }
}
